package com.boot.jwt.util;/**
 * @description
 * @autor xbwu on 2019/3/15.
 */

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;
import java.util.List;

/**
 * jwt工具类
 * 绝大多数情况下，传统的 cookie-session 机制工作得更好。
 * jwt 适合做简单的 restful api 认证，颁发一个固定有效期的 jwt，降低 jwt 暴露的风险，不要对 jwt 做服务端的状态管理，这样才能体现出 jwt 无状态的优势。
 * 推荐查看：https://blog.csdn.net/qq_28165595/article/details/80214994 再确定jwt的使用场景
 * 原则上，jwt由于只是增加一个可以验证的签名，对数据内容不做加密，任何人都可以使用JWT.decode(token)进行解密。
 * 而且jwt没有机制做一次性使用签名，比如支付签名。
 * 使用传统的session来保持用户状态，用redis等第三方共享session可以获得更好地使用效果（本质上jwt token和session id都是存放在cookie上，
 * 那么token的长度可比session id长多了）
 * 而且session有以下特性：自带刷新和失效实现。
 * 如果换成jwt来实现的话，刷新，可以通过下面的addTokenTimeAndCheck(token)，来增加有效时间。
 * 但是jwt没有办法强行失效，只能通过修改签名数据来使之失效。
 *
 * 实际上jwt接近是一个无状态的签名验证工具，当然还可以有一个固定的有效期。
 * 建议：在pc端上的用户会话，使用session，如果服务器集群，则加上redis，在手机端上的用户会话，使用oauth2框架。
 * @author xbwu
 * @create 2019-03-15 
 **/
public final class JWTUtils {

    /**
     * token hmac加密密匙 建议密匙应该设计成和用户相关的属性，避免出现用户修改密码、注销后，原来的token还是有效的问题
     */
    private final static String SECRET="wuxb";
    /**
     * 获得token
     * @param params 需要放入token的参数
     * @return
     */
    public final static String getTokenByHMAC(String[] params) {
        String token= JWT.create()
                .withIssuer(SECRET)
                //签名时间（verify时会验证，必须小于当前时间）
                .withIssuedAt(new Date())
                //过期时间（verify时会验证，必须大于当前时间）
                //.withExpiresAt(new Date())
                //开始使用时间（verify时会验证，必须小于当前时间）
                //.withNotBefore(new Date())
                .withAudience(params)
                .sign(Algorithm.HMAC256(SECRET));
        return token;
    }

    /**
     * 延期token校验器，来判断token是否有效
     * @param leeway 增加token开始使用日期和1秒的jwt的签发时间，单位：秒
     * @param expires 增加token有效日期，单位：秒
     * @param token
     * @return
     */
    public final static boolean addTokenTimeAndCheck(Long leeway,Long expires,String token){
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET))
                .acceptLeeway(leeway==null?0:leeway)
                .acceptExpiresAt(expires==null?0:expires)
                .build();
        try {
            DecodedJWT jwt = verifier.verify(token);
        }catch (JWTVerificationException e){
            return false;
        }
        return true;
    }

    /**
     * 验证token是否有效
     * @param token
     * @return
     */
    public final static boolean checkTokenValid(String token){
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer(SECRET)
                .build(); //Reusable verifier instance
        try {
            DecodedJWT jwt = verifier.verify(token);
        }catch (JWTVerificationException e){
            return false;
        }
        return true;
    }

    /**
     * 获取token中存放的参数
     * @param token
     * @return
     */
    public final static List<String> getTokenParam(String token){
        return JWT.decode(token).getAudience();
    }
}
